/*
********************************************************************************************************
文件名：bsp_iic.c
功  能：在访问I2C设备前，请先调用 i2c_CheckDevice() 检测I2C设备是否正常，该函数会配置GPIO
备  注：模拟iic
淘  宝：https://shop60670850.taobao.com
作  者：lilianhe
日  期: 2017-2-6
*********************************************************************************************************
*/
#include "bsp_iic.h"

/*
********************************************************************************************************
更新日志：
1.2017-2-8，使用PB10----SCL，PB11----SDA


*********************************************************************************************************
*/

/*
*********************************************************************************************************
*       函 数 名: i2c_Delay
*       功能说明: I2C总线位延迟，最快400KHz
*       形    参：无
*       返 回 值: 无
*********************************************************************************************************
*/
void i2c_Delay(void)
{
        uint8_t i;

        /*　
                下面的时间是通过安富莱AX-Pro逻辑分析仪测试得到的。
                CPU主频72MHz时，在内部Flash运行, MDK工程不优化
                循环次数为10时，SCL频率 = 205KHz 
                循环次数为7时，SCL频率 = 347KHz， SCL高电平时间1.5us，SCL低电平时间2.87us 
                循环次数为5时，SCL频率 = 421KHz， SCL高电平时间1.25us，SCL低电平时间2.375us 
        
    IAR工程编译效率高，不能设置为7
        */
        for (i = 0; i < 10; i++);
}

/*
*********************************************************************************************************
*       函 数 名: i2c_Start
*       功能说明: CPU发起I2C总线启动信号
*       形    参：无
*       返 回 值: 无
*********************************************************************************************************
*/
void IIC_Start(void)
{
        /* 当SCL高电平时，SDA出现一个下跳沿表示I2C总线启动信号 */
        I2C_SDA_1();
        I2C_SCL_1();
        i2c_Delay();
        I2C_SDA_0();
        i2c_Delay();
        I2C_SCL_0();
        i2c_Delay();
}

/*
*********************************************************************************************************
*       函 数 名: i2c_Start
*       功能说明: CPU发起I2C总线停止信号
*       形    参：无
*       返 回 值: 无
*********************************************************************************************************
*/
void IIC_Stop(void)
{
        /* 当SCL高电平时，SDA出现一个上跳沿表示I2C总线停止信号 */
        I2C_SDA_0();
        I2C_SCL_1();
        i2c_Delay();
        I2C_SDA_1();
}

/*
*********************************************************************************************************
*       函 数 名: i2c_SendByte
*       功能说明: CPU向I2C总线设备发送8bit数据
*       形    参：_ucByte ： 等待发送的字节
*       返 回 值: 无
*********************************************************************************************************
*/
void IIC_Send_Byte(uint8_t _ucByte)
{
        uint8_t i;

        /* 先发送字节的高位bit7 */
        for (i = 0; i < 8; i++)
        {               
                if (_ucByte & 0x80)
                {
                        I2C_SDA_1();
                }
                else
                {
                        I2C_SDA_0();
                }
                i2c_Delay();
                I2C_SCL_1();
                i2c_Delay();    
                I2C_SCL_0();
                if (i == 7)
                {
                         I2C_SDA_1(); // 释放总线
                }
                _ucByte <<= 1;  /* 左移一个bit */
                i2c_Delay();
        }
}

/*
*********************************************************************************************************
*       函 数 名: i2c_ReadByte
*       功能说明: CPU从I2C总线设备读取8bit数据
*       形    参：无
*       返 回 值: 读到的数据
*********************************************************************************************************
*/
uint8_t IIC_Read_Byte(u8 ack)
{
        uint8_t i;
        uint8_t value;

        /* 读到第1个bit为数据的bit7 */
        value = 0;
        for (i = 0; i < 8; i++)
        {
                value <<= 1;
                I2C_SCL_1();
                i2c_Delay();
                if (I2C_SDA_READ())
                {
                        value++;
                }
                I2C_SCL_0();
                i2c_Delay();
        }
        if (!ack)
        IIC_NAck();//??nACK
    else
        IIC_Ack(); //??ACK  
        return value;
}

/*
*********************************************************************************************************
*       函 数 名: i2c_WaitAck
*       功能说明: CPU产生一个时钟，并读取器件的ACK应答信号
*       形    参：无
*       返 回 值: 返回0表示正确应答，1表示无器件响应
*********************************************************************************************************
*/
uint8_t IIC_Wait_Ack(void)
{
        uint8_t re;

        I2C_SDA_1();    /* CPU释放SDA总线 */
        i2c_Delay();
        I2C_SCL_1();    /* CPU驱动SCL = 1, 此时器件会返回ACK应答 */
        i2c_Delay();
        if (I2C_SDA_READ())     /* CPU读取SDA口线状态 */
        {
                re = 1;
        }
        else
        {
                re = 0;
        }
        I2C_SCL_0();
        i2c_Delay();
        return re;
}

/*
*********************************************************************************************************
*       函 数 名: i2c_Ack
*       功能说明: CPU产生一个ACK信号
*       形    参：无
*       返 回 值: 无
*********************************************************************************************************
*/
void IIC_Ack(void)
{
        I2C_SDA_0();    /* CPU驱动SDA = 0 */
        i2c_Delay();
        I2C_SCL_1();    /* CPU产生1个时钟 */
        i2c_Delay();
        I2C_SCL_0();
        i2c_Delay();
        I2C_SDA_1();    /* CPU释放SDA总线 */
}

/*
*********************************************************************************************************
*       函 数 名: i2c_NAck
*       功能说明: CPU产生1个NACK信号
*       形    参：无
*       返 回 值: 无
*********************************************************************************************************
*/
void IIC_NAck(void)
{
        I2C_SDA_1();    /* CPU驱动SDA = 1 */
        i2c_Delay();
        I2C_SCL_1();    /* CPU产生1个时钟 */
        i2c_Delay();
        I2C_SCL_0();
        i2c_Delay();    
}

/*
*********************************************************************************************************
*       函 数 名: i2c_CfgGpio
*       功能说明: 配置I2C总线的GPIO，采用模拟IO的方式实现
*       形    参：无
*       返 回 值: 无
*********************************************************************************************************
*/
void IIC_Init(void)
{
        GPIO_InitTypeDef GPIO_InitStructure;

        RCC_APB2PeriphClockCmd(RCC_I2C_PORT, ENABLE);   /* 打开GPIO时钟 */

        GPIO_InitStructure.GPIO_Pin = I2C_SCL_PIN | I2C_SDA_PIN;
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;        /* 开漏输出 */
        GPIO_Init(GPIO_PORT_I2C, &GPIO_InitStructure);

        /* 给一个停止信号, 复位I2C总线上的所有设备到待机模式 */
        IIC_Stop();
}

/*
*********************************************************************************************************
*       函 数 名: i2c_CheckDevice
*       功能说明: 检测I2C总线设备，CPU向发送设备地址，然后读取设备应答来判断该设备是否存在
*       形    参：_Address：设备的I2C总线地址
*       返 回 值: 返回值 0 表示正确， 返回1表示未探测到
*********************************************************************************************************
*/
uint8_t i2c_CheckDevice(uint8_t _Address)
{
        uint8_t ucAck;

        IIC_Init();             /* 配置GPIO */

        
        IIC_Start();            /* 发送启动信号 */

        /* 发送设备地址+读写控制bit（0 = w， 1 = r) bit7 先传 */
        IIC_Send_Byte(_Address | I2C_WR);
        ucAck = IIC_Wait_Ack(); /* 检测设备的ACK应答 */

        IIC_Stop();                     /* 发送停止信号 */

        return ucAck;
}

